import os
import socket
import struct
import threading
import os

from chat.server.code import SendEmail
from chat.server.database import dbs
from chat.common import config
from chat.common import RSA_file

user_socket = []
port = 2335


class server_thread(threading.Thread):
    def __init__(self, conn, address):
        threading.Thread.__init__(self)
        self.conn = conn
        self.address = address

    def run(self):
        conn = self.conn
        address = self.address
        data_len = conn.recv(4)
        data_len = int.from_bytes(data_len, 'little')
        data_type = conn.recv(4)
        data_type = int.from_bytes(data_type, 'little')
        print("__init__ 28 [data_type] ==> ", data_type)
        db = dbs()
        # print(data_type == 0x00)
        if data_type == 0:  # 用户注册

            user_len, email_len, passwd_len, code_len, pubkey_len, pubkey_n_len = struct.unpack("<HHHHII",
                                                                                                conn.recv(16))

            username = conn.recv(user_len).decode()
            email = conn.recv(email_len).decode()
            passwd = conn.recv(passwd_len).decode()
            code = conn.recv(code_len).decode()
            pubkey = conn.recv(pubkey_len).decode()
            pubkey_n = conn.recv(pubkey_n_len).decode()
            # print(username, passwd, email, code, key)

            head = struct.pack("<i", 2) + b'\x00\x00\x00\x00'
            # 数据库查重
            if db.select_username_repeat(username):  # 用户名已注册
                data = b'\x01\x00'
                conn.sendall(head + data)
                return
            if db.select_email_repeat(email):  # 邮箱已注册
                data = b'\x02\x00'
                conn.sendall(head + data)
                return

            # 验证码检测
            if not db.select_code_right(email, code):  # 验证码错误
                data = b'\x03\x00'
                conn.sendall(head + data)
                return

            # 写入数据库
            db.insert_user_information(username, passwd, email, pubkey, pubkey_n)
            # 注册成功
            data = b'\x11'
            userID = str(db.query_userID_byname(username)).encode()
            conn.sendall(head + userID + data)

            global user_socket
            send_id = []
            for user in user_socket:
                for key in user.keys():
                    send_id.append(key)
            for id in send_id:
                name = db.query_username_byuserID(id)
                flash_user(id, name)
            return

        elif data_type == 1:  # 用户名登录  登陆成功未结束线程

            user_len, passwd_len = struct.unpack("<HH", conn.recv(4))

            username = conn.recv(user_len).decode()
            passwd = conn.recv(passwd_len).decode()
            print("username>>", username)
            print("passwd>>", passwd)

            head = struct.pack("<i", 2) + b'\x01\x00\x00\x00'

            # 数据库验证
            if not db.select_password_right(username, passwd):  # 登录验证失败
                data = b'\x01\x00'
                conn.sendall(head + data)
                return

            data = b'\x00\x00'
            conn.sendall(head + data)

            user_id = db.query_userID_byname(username)
            add_socket(user_id, address[0])
            print(conn.recv(7).decode())
            flash_user(user_id, username)
            deal_socket(conn)

        elif data_type == 2:  # 邮箱登录

            email_len, code_len = struct.unpack("<HH", conn.recv(4))
            email = conn.recv(email_len).decode()
            code = conn.recv(code_len).decode()

            username = db.query_username_byemail(email)

            if not db.select_code_right(email, code):  # 登录验证失败
                data = b'\x01\x00'
                head = struct.pack("<i", 2) + b'\x02\x00\x00\x00'
                conn.sendall(head + data)
                return

            data = b'\x00\x00'
            data += struct.pack("<H", len(username))
            data += str.encode(username)
            head = struct.pack("<i", len(data)) + b'\x02\x00\x00\x00'
            conn.sendall(head + data)

            user_id = db.query_userID_byemail(email)
            add_socket(user_id, address[0])
            print(conn.recv(7).decode())
            flash_user(user_id, username)
            deal_socket(conn)

        elif data_type == 3:  # 邮箱验证码

            email = conn.recv(data_len).decode()
            ecode = SendEmail(receiver=email)
            temp_code = ecode.generate_verification()  # 生成6位随机数验证码

            # 清理无用数据
            db.clear_cache()
            # 将邮箱与验证码写入对应用户数据库
            db.insert_email_information(email)
            db.insert_user_code(temp_code, email)
            # 发送验证码邮件
            ecode.send_email(temp_code)
            return


def server_socket():
    conf = config.get_server_config()
    # print(conf)
    s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((conf['bind_ip'], conf['bind_port']))
    s.listen(1)

    print("Server listening on " + conf['bind_ip'] + ":" + str(conf['bind_port']))

    # bytes_to_receive = {}
    # bytes_received = {}
    # data_buffer = {}
    db = dbs()
    db.init_database()

    threads = []
    # 创建新线程
    while True:
        conn, address = s.accept()
        st = server_thread(conn, address)
        st.start()
        threads.append(st)


def add_socket(user_id, address):
    global user_socket, port
    user_socket.append({user_id: (address, port)})


def get_socket(user_address):
    s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)  # 创建套接字
    s.connect(user_address)  # 连接服务器
    return s

def flash_user(userID, username):
    print("__init__.flash.user()")
    db = dbs()
    user_info = db.query_all_other_users(username)
    room_info = db.query_room_information(username)
    user_num = len(user_info)
    room_num = len(room_info)
    data = struct.pack("<HH", user_num, room_num)
    user_id = []
    user_name = []
    room_id = []
    room_inf = []

    for key in user_info.keys():
        user_id.append(key)
    for value in user_info.values():
        user_name.append(value)
    for i in range(user_num):
        data += struct.pack("<HH", user_id[i], len(user_name[i]))
        data += str.encode(user_name[i])

    for key in room_info.keys():
        room_id.append(key)
    for value in room_info.values():
        room_inf.append(value)
    for i in range(room_num):
        data += struct.pack("<HH", room_id[i], len(room_inf[i][1]))
        data += str.encode(room_inf[i][1])
        data += struct.pack("<iii", len(room_inf[i][2]), len(room_inf[i][3]), len(room_inf[i][4]))
        data += room_inf[i][2].encode()
        data += room_inf[i][3].encode()
        data += room_inf[i][4].encode()

    key = db.query_public_key_public_key_n(userID)
    pubkey = int(key[0])
    n = int(key[1])
    data = RSA_file.message_encryption(data.decode(), pubkey, n).encode()
    head = struct.pack("<i", len(data)) + b'\x06\x00\x00\x00'

    msg = head + data
    print(msg)
    send_change(userID, msg)

def send_file(userID, msg_id, room_id, sender_id):
    global user_socket
    print("__init__.215", user_socket)
    db = dbs()
    filepath = db.query_data_by_msg_id(msg_id)
    for i in user_socket:
        if userID in i:
            print("找到一个连接", userID)

            user_address = i[userID]
            print("address=>", user_address)
            # print(data)
            s = get_socket(user_address)

            if os.path.isfile(filepath):
                # 定义文件头信息，包含文件名和文件大小
                file_head = struct.pack('<HHHl', room_id, sender_id, len(os.path.basename(filepath)),
                                        os.stat(filepath).st_size)
                file_head += os.path.basename(filepath).encode()
                head = struct.pack("<i", len(file_head)) + b'\x09\x00\x00\x00'

                # 发送文件名称与文件大小
                s.send(head + file_head)

                # 将传输文件以二进制的形式分多次上传至服务器
                fp = open(filepath, 'rb')
                while 1:
                    data = fp.read(1024)
                    if not data:
                        print('{0} file send over...'.format(os.path.basename(filepath)))
                        break
                    s.send(data)

def send_change(userID, data):
    global user_socket
    print("__init__.215",user_socket)
    for i in user_socket:
        if userID in i:

            print("找到一个连接", userID)

            user_address = i[userID]
            print("address=>", user_address)
            print(data)
            s = get_socket(user_address)
            s.send(data)
            s.close()


def deal_socket(conn):
    # print(conn)
    while True:

        data_len = conn.recv(4)
        data_len = int.from_bytes(data_len, 'little')
        data_type = conn.recv(4)
        data_type = int.from_bytes(data_type, 'little')
        # print("data_type==>", data_type)

        if data_type == 4:  # 创建room
            user_num = conn.recv(2)
            # print(tmp)
            user_num = struct.unpack("<H", user_num)
            user_num = user_num[0]
            print(user_num)
            user_id = []
            for i in range(user_num):
                user_id.append(struct.unpack("<H", conn.recv(2))[0])
            print(user_id)
            db = dbs()
            new_room = RSA_file.USR()

            # 创建room 写入两个公钥new_room.pubkey,new_room.n member写入user_id 生成room_name 格式为 xx-xx-xx... 返回room_id,room_name
            room_info = db.insert_room(user_id, new_room.pubkey, new_room.n, new_room.privkey)

            room_id = room_info[0]
            room_name = str(room_info[1]).encode()
            pvk = str(new_room.privkey).encode()
            pbk = str(new_room.pubkey).encode()
            n = str(new_room.n).encode()

            data_head = struct.pack("<HiiiH", room_id, len(pbk), len(pvk), len(n), len(room_name))
            print(data_head)
            data = data_head
            data += pbk + pvk + n + room_name


            for i in user_id:
                pubkey = db.query_public_key_public_key_n(i)
                user_data = RSA_file.message_encryption(data.decode(), int(pubkey[0]), int(pubkey[1])).encode()
                # print(user_data)
                head = struct.pack("<i", len(user_data)) + b'\x04\x00\x00\x00'
                print(len(user_data))
                msg = head + user_data
                send_change(i, msg)
                username = db.query_username_byuserID(i)
                flash_user(i, username)

        elif data_type == 5:  # 发送消息
            print("============发送消息=============")

            data_head = conn.recv(12)
            room_id, sender_id, msg_len = struct.unpack("<HHQ", data_head)
            enc_msg = conn.recv(msg_len).decode()
            flag = conn.recv(1)
            OTP_flag = int.from_bytes(flag, 'little')

            # 写入服务器数据库
            db = dbs()
            msg_id = db.insert_history_information(sender_id, room_id, enc_msg)
            db.insert_OTPflag(msg_id, OTP_flag)

            data_head = struct.pack("<HHQ", room_id, sender_id, msg_len)
            data = data_head + enc_msg.encode() + flag
            head = struct.pack("<i", len(data)) + b'\x05\x00\x00\x00'
            msg = head + data

            # 查询room_id下的所有user_id
            user_id_list = db.query_user_id_by_room_id(room_id)

            for i in user_id_list:
                send_change(i, msg)

        elif data_type == 7:  # 获取历史

            db = dbs()
            room_id = conn.recv(2)
            room_id = struct.unpack("<H", room_id)[0]
            print("__init__.296", room_id)
            his_list = db.query_all_history_information(room_id)
            msg_num = len(his_list)

            conn.send(struct.pack("<i", room_id))
            conn.send(b'\x07\x00\x00\x00')
            conn.send(struct.pack("<i", msg_num))
            print("__init__.306")
            for his in his_list:
                sender_id = his[1]
                print(sender_id)
                time = his[3]
                print(time)
                msg = his[4]
                print(msg)
                msg_len = len(msg)
                OTP_flag = his[5]
                print("__inti__.37")
                print(OTP_flag)
                flag = b'\x00'
                if OTP_flag == 1:
                    flag = b'\x01'
                elif OTP_flag == 3:
                    flag = b'\x03'
                print(flag)
                data_head = struct.pack("<H19sQ", sender_id, time.encode(), msg_len)
                conn.send(data_head)
                conn.send(msg.encode())
                conn.send(flag)

        elif data_type == 8:   # 退出登录
            user_id = struct.unpack("<H", conn.recv(2))
            exit = conn.recv(4)
            if exit.decode() == "exit":
                global user_socket
                for i in user_socket:
                    if user_id in i.keys():
                        del user_socket[i]
                        break

        elif data_type == 9:        # 发送文件0
            file_head = conn.recv(10)
            room_id, sender_id, filename_len, file_size = struct.unpack('<HHHl', file_head)
            filename = conn.recv(filename_len).decode()

            print('file new name is {0}, file_size if {1}'.format(str(filename), file_size))

            recvd_size = 0  # 定义已接收文件的大小
            # 存储在该脚本所在目录下面
            fp = open('./upload/' + str(filename), 'wb')
            print('start receiving...')

            # 将分批次传输的二进制流依次写入到文件
            while not recvd_size == file_size:
                if file_size - recvd_size > 1024:
                    data = conn.recv(1024)
                    recvd_size += len(data)
                else:
                    data = conn.recv(file_size - recvd_size)
                    recvd_size = file_size
                fp.write(data)
            fp.close()
            print('end receive...')

            db = dbs()
            msg_id = db.insert_history_information(sender_id, room_id, "./upload/"+str(filename))
            db.insert_OTPflag(msg_id, 3)

            # 查询room_id下的所有user_id
            user_id_list = db.query_user_id_by_room_id(room_id)

            for i in user_id_list:
                send_file(i, msg_id, room_id, sender_id)

        elif data_type == 10: # 删除room
            print("rm room")
            # 4bytes_len + \x0A\x00\x00\x00 + room_id_num[4bytes] + room_id[2bytes]* room_id_num
            room_id_num = conn.recv(4)
            room_id_num = struct.unpack("<i", room_id_num)[0]
            print("0 delete room_id ==> ", room_id_num)
            db = dbs()
            for i in range(room_id_num):
                room_id = conn.recv(2)
                print("1 delete room_id ==> ", room_id)
                room_id = struct.unpack("<H", room_id)[0]
                print("2 delete room_id ==> ", room_id)
                db.delete_room(room_id)


server_socket()
